﻿using System.Collections;
using System.IO;
using Core.Extensions;
using EmbeddedWebserver.Core.Handlers.Abstract;
using Microsoft.SPOT;

namespace EmbeddedWebserver.Core.Handlers
{
    internal sealed class FileSystemHandler : HandlerBase
    {
        #region Non-public members

        private static readonly Hashtable SupportedTypes = new Hashtable
                                                               {
            { ".htm", null },
            { ".html", null },
            { ".js", null },
            { ".css", null },
            { ".gif", null },
            { ".jpg", null },
            { ".png", null },
            { ".bmp", null }
        };

        private static string BuildTargetUrl(HttpContext pContext)
        {
            return pContext.Server.MapPath(pContext.Request.Url.TrimStart('/'));
        }

        internal static bool CanHandleRequest(HttpContext pContext)
        {
            if (pContext.Server.ApplicationConfiguration.Path.IsNullOrEmpty())
            {
                return false;
            }
            string targetUri = BuildTargetUrl(pContext);
            var rval = ((pContext.Server.ApplicationConfiguration.EnableDirectoryBrowsing && Directory.Exists(targetUri)) || (SupportedTypes.Contains(Path.GetExtension(targetUri).ToLower()) && File.Exists(BuildTargetUrl(pContext))));
            if (rval)
            {
                Debug.Print(targetUri);
            }
            return rval;
        }

        private static void ServeFileContent(string pTargetUri, HttpContext pContext)
        {
            var targetUrl = BuildTargetUrl(pContext);
            pContext.Response.ResponseStream = File.Open(targetUrl, FileMode.Open, FileAccess.Read);
            var extension = Path.GetExtension(pTargetUri);
            pContext.Response.ContentType = pContext.Server.MapContentType(extension);
            //DebugHelper.Print(String.Concat("ServeFileContent ", targetUrl, ", content type", pContext.Response.ContentType));
        }

        private static void BuildDirectoryContentItem(StringBuilder pResponseBuilder, string pRequestUrl, FileSystemInfo pFileInfo, bool pIsDirectory, IHttpServerUtility pServerUtility)
        {
            pResponseBuilder.Append(pFileInfo.LastWriteTime.ToString("yyyy.MM.dd.    hh.mm."));
            pResponseBuilder.Append("        ");
            pResponseBuilder.Append((pIsDirectory ? StringExtensions.PadRight("&lt;dir&gt;", 19) : StringExtensions.PadRight(((FileInfo)pFileInfo).Length.ToString(), 13)));
            pResponseBuilder.Append(" <a href=\"");
            pResponseBuilder.Append(pServerUtility.UrlEncode(pRequestUrl + "/" + pFileInfo.Name));
            pResponseBuilder.Append("\">");
            pResponseBuilder.Append(pFileInfo.Name);
            pResponseBuilder.Append("</a><br>");
        }

        private static void ServeDirectoryContent(string pTargetUri, HttpContext pContext)
        {
            var baseDir = new DirectoryInfo(pTargetUri);
            string renderPath = (pContext.Request.Url.IsNullOrEmpty() ? "" : "/") + pContext.Request.Url;
            var responseBuilder = new StringBuilder(HtmlDoctype);
            responseBuilder.Append("<html><head><title>");
            responseBuilder.Append(renderPath);
            responseBuilder.Append("/</title></head><body><h1>");
            responseBuilder.Append(renderPath);
            responseBuilder.Append("/</h1><hr><pre>");
            if (baseDir.FullName.Trim('\\') != pContext.Server.ApplicationConfiguration.Path.Trim('\\'))
            {
                int parentSeparatorIndex = pContext.Request.Url.LastIndexOf('/');
                string parentUri = "";
                if (parentSeparatorIndex > 0)
                {
                    parentUri = pContext.Request.Url.Substring(0, parentSeparatorIndex);
                }
                responseBuilder.Append("<a href=\"/");
                responseBuilder.Append(pContext.Server.UrlEncode(parentUri));
                responseBuilder.Append("\">[To Parent Directory]</a><br>");
            }
            foreach (DirectoryInfo dir in baseDir.GetDirectories())
            {
                BuildDirectoryContentItem(responseBuilder, pContext.Request.Url, dir, true, pContext.Server);
            }
            foreach (FileInfo file in baseDir.GetFiles())
            {
                BuildDirectoryContentItem(responseBuilder, pContext.Request.Url, file, false, pContext.Server);
            }
            responseBuilder.Append("</pre><hr></body></html>");
            pContext.Response.ResponseBody = responseBuilder.ToString();
        }

        protected override void ProcessRequestWorker(HttpContext pContext)
        {
            string targetUri = BuildTargetUrl(pContext);

            if (Directory.Exists(targetUri))
            {
                ServeDirectoryContent(targetUri, pContext);
            }
            else
            {
                ServeFileContent(targetUri, pContext);
            }
        }

        #endregion

        #region Constructors

        public FileSystemHandler() : base(HttpMethods.GET) { }

        #endregion
    }
}
